package algoritmos.numericos.tipos;

import java.util.ArrayList;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import Jama.Matrix;
import algoritmos.Matriz;
import algoritmos.numericos.Supervisado;

public class LVQS extends Supervisado{	
	/**
	 * kMax
	 */
	private int kMax;
	
	private boolean MuestraIter;

	public LVQS(JTextArea arg0, JProgressBar arg1, JButton arg2, JButton arg3){
		super();
		MuestraIter = false;
		texto = arg0;
		progreso = arg1;
		botonNuevoDato = arg2;
		botonAprendizaje = arg3;
		botonVisualizaGraficos = null;
	}
	
	protected void aprendizaje() {
		double e = (double)0.0001;
		int k;

		if (centros.size() == 0)
			return;
		
		for (k = 1; k <= kMax; k++)
		{
			ArrayList<Matrix> viejoCentros = copiaCentros();
			ArrayList<Matrix> patronesEntrenamiento = dameMuestrasDesordenadas();
			
			if (MuestraIter)
				salida("*** Iteración "+ k);

			for(Matrix temp : patronesEntrenamiento)
			{
				Matriz muestraPE = (Matriz) temp;
				
				int indexCentro = muestraPerteneceAClase(muestraPE);
				
				actualizaCentro(indexCentro, muestraPE, 1/(10+(double)k));
			}
			
			if(stopAprendizaje(viejoCentros, centros, e))
				break;
		}

		long milis = new Date().getTime() - startTime.getTime();
		String tmp;

		salida("Proceso de aprendizaje finalizado");
		if (milis > 1000){ // Si ha tardado más de un segundo
			tmp = (milis / 1000) + " segundos " + milis % 1000 + " ms.";
		} else { // Si ha tardado milisegundos
			tmp = milis + " ms.";
		}
		salida("El proceso ha durado " + tmp);
		if (k >= kMax)
			salida("Se han dado " + kMax + " iteraciones");
		else
			salida("Se han dado " + k + " iteraciones");
		salida("Centros de las clases:");
		for (int i = 0; i < numeroDeClases(); i++)
			salida("Clase " + (i+1) + "= " + centros.get(i).toString());
		salida("");
		finalizaComponentesAprendizaje();
	}

	protected boolean convergencia(){
		return true;
	}
	
	private void actualizaCentro(int indexClase, Matriz Muestra, double factor){
		Matrix centro = centros.get(indexClase);

		if (MuestraIter)
			salida ("Centro " + indexClase);
		for (int i = 0; i < centro.getColumnDimension(); i++) {
			double old = centro.get(0, i);
			centro.set(0, i, centro.get(0, i) + 
					factor * (Muestra.get(0, i) - centro.get(0, i)));
			if (MuestraIter)
				salida ("[" + i + "]: " + old + ", " + centros.get(indexClase).get(0, i));
		}
		centros.set(indexClase, centro);
	}

/*	private void actualizaCentro(int indexCentroActualizar, Float[] muestra, float factorAprendizaje)
	{
		Float[] centroActualizar = (Float[])centros.get(indexCentroActualizar);
		
		for(int i = 0 ; i < centroActualizar.length; i++)
		{
			centroActualizar[i] = centroActualizar[i] + (factorAprendizaje * (muestra[i] - centroActualizar[i]));
		}
	}*/

	private ArrayList<Matrix> copiaCentros() {
		ArrayList<Matrix> Res = new ArrayList<Matrix>();
		
		for (int i = 0; i < centros.size(); i++)
			Res.add((Matrix)centros.get(i).clone());
			//Res.set(i, centros.get(i));
			
		return Res;
	}
	
	private ArrayList<Matrix> dameMuestrasDesordenadas() {
		ArrayList<Matrix> Res = new ArrayList<Matrix>();

		for (int i = 0; i < entrada.size(); i++)
			Res.add(null);
		for (int i = 0; i < entrada.size(); i++) {
			int dest = (int)(Math.random() * (entrada.size()-1));
			while (Res.get(dest) != null)
				dest = (dest+1) % Res.size();
			Res.set(dest, entrada.get(i));
		}
		
		return Res;
	}
	
	private boolean stopAprendizaje(ArrayList<Matrix> viejoCentros, 
			ArrayList<Matrix> centros, double e) {
		boolean Res = true;
		
		for (int i = 0; i < viejoCentros.size(); i++) {
			Matriz centro = (Matriz) viejoCentros.get(i);
			double distancia = Math.sqrt(centro.distancia((Matriz)centros.get(i)));
			if (distancia >= e) {
				Res = false;
				break;
			}
		}
		
		return Res;
	}
		
	private int muestraPerteneceAClase(Matriz muestra){
		//Chequeo que haya clases
		if(centros.size() == 0)
			return -1;
		
		double minDistancia = Math.sqrt(muestra.distancia((Matriz)centros.get(0))); 
		int index = 0;
		
		for(int i = 1; i < centros.size(); i++)
		{
			double distanciaActual = Math.sqrt(muestra.distancia((Matriz)centros.get(i)));
			
			if(distanciaActual < minDistancia)
			{
				minDistancia = distanciaActual;
				index = i;
			}
		}
		
		return index;
	}

	public void perteneceAClase(ArrayList<Number> lista){
		int clase;
		
		Matriz dato = new Matriz(lista);
		
		clase = muestraPerteneceAClase(dato) + 1;
		
		if (clase == 0)
			salida("La muestra " + dato + " no se conoce clase contenedora.");
		else
			salida("La muestra " + dato + " pertenece a la clase " + clase + ".");
	}

	private void salida(String St){
		final String mensaje = St;
		SwingUtilities.invokeLater(new Runnable(){
			@Override
			public void run() {
				texto.append(mensaje + "\n");
			}
		});
	}

	/*=========================================================================*
	 *							Accesores y mutadores                          * 
	 *=========================================================================*/
	
	public int getkMax() {
		return kMax;
	}

	public void setkMax(int kMax) {
		this.kMax = kMax;
	}
}